
#include "server.h"
#include "sock.h"
#include "http.h"
#include "cache_file.h"
#include "conf.h"
#include "globals.h"
#include "common.h"

FILE *error_logp;
FILE *warn_logp;
FILE *debug_logp;
FILE *access_logp;
config_t *conf;
thread_t *threads;
int nworker;                         /* number of worker. Not include master thread */
struct event_base *master_base;
struct timeval current_time;
pthread_mutex_t cache_file_table_lock;

static pthread_mutex_t accept_lock;

typedef void SGHANDLE(int);

static void parse_options(int argc, char **argv);
static void usage(void) ;
static void conn_accept_handle(int fd, short flag, void *arg);
static void signal_register(int sig, SGHANDLE *func, int flags);
static void server_down(int signum);


void current_time_get(void)
{
	if(-1 == gettimeofday(&current_time, NULL)) {
		error_log("%d\n", current_time.tv_sec);
	}
}

int main(int argc, char **argv)
{
	int listen_fd;
	struct sockaddr_in srv_addr;
	conn_t *srv_con;

	config_default();
	log_init();

	pthread_mutex_init(&accept_lock, NULL);

	/* parse options */
	parse_options(argc, argv);
	/* parse config file */
	parse_conf();
	
	cache_file_table_init();

	cache_dir_init();
	/* signal handle */
	signal_register(SIGINT, server_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
	signal_register(SIGTERM, server_down, SA_NODEFER | SA_RESETHAND | SA_RESTART);
	signal_register(SIGPIPE, SIG_IGN, SA_RESTART);
	
	/* init listen fd */
	master_base = event_init();
	assert(master_base);

	srv_addr.sin_family = AF_INET;
	srv_addr.sin_port = conf->server.port;
	srv_addr.sin_addr = conf->server.bind_addr4;
	listen_fd = sock_open();
	sock_bind(listen_fd, (struct sockaddr *)&srv_addr, sizeof(srv_addr));
	sock_listen(listen_fd, 5);
	
	sock_reuseaddr(listen_fd);

	srv_con = conn_new(listen_fd, EV_READ | EV_PERSIST, conn_accept_handle, \
					 NULL, NULL, master_base, &threads[0]);
	/* setup thread */
	thread_init(conf->server.worker);

	event_base_loop(master_base, 0);

	return 0;  /* not reached */
}

static void parse_options(int argc, char **argv)
{
	assert(conf);
	int c;
	
	while(-1 != (c = getopt(argc, argv, "hvD:f:"))) {
		switch(c) {
			case 'f':
				if(!optarg) 
					usage();
				if(conf->log.conf) {
					free(conf->log.conf);
					conf->log.conf = strdup(optarg);
				}
				else {
					conf->log.conf = strdup(optarg);
				}
				break;
			case 'h':
				usage();
				break;
			case 'D':
				conf->server.debug_mode = 1;
				if(optarg) 
					conf->server.debug_level = atoi(optarg);
				break;
			case 'v': 
				fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
				exit(1);
				break;
			default:
				break;
		}
	}
}

static void usage(void) 
{
	fprintf(stderr, "Usage: aladin [options]\n" \
					"Options: \n" \
					"       -D level                   Debug mode.\n" \
					"       -f config                  Setting config file.\n"\
					"       -h                         Help information.\n"\
					"       -v                         Print version.\n");
	exit(1);
}


static void conn_accept_handle(int fd, short flag, void *arg)
{
	conn_t *con;
	struct sockaddr_in client_addr;
	int c_fd;
	thread_t *thread;
	queue_item_t *item;
	accept_t *at ;

	c_fd = sock_accept(fd, (struct sockaddr *)&client_addr, sizeof(client_addr));
	if(c_fd == -1)  {
		debug_log(3, "fd %d accept failed\n", fd);
		return;
	}
	
	debug_log(3, "fd %d accept\n", fd);
	sock_noblocking_set(c_fd);
	
	thread = thread_select();
	at = mempool_alloc(sizeof(*at));
	if(!at)
		return;
	at->fd = c_fd;
	at->flag = EV_READ|EV_PERSIST;
	at->data = client_addr.sin_addr;
	at->handler = http_request_read;

	debug_log(3, "fd %d, flag %d, data %p, handler %p add\n", at->fd, at->flag, at->data, at->handler);
	item = queue_item_init(at);
	assert(item);

	queue_push(thread->conns, item);
	
	/* notify worker */
	if(write(thread->notify_send_fd, "", 1) == -1)
		error_log("notify %u failed\n", thread->id);
}


static void server_down(int signum)
{   
		debug_log(1, "signum %d\n", signum);
		
		if(access_logp)
			fflush(access_logp);
		
		cache_dir_close();

		exit(1);
}


static void signal_register(int sig, SGHANDLE *func, int flags)
{
#if HAVE_SIGACTION
	struct sigaction sa;
	sa.sa_handler = func;
	sa.sa_flags = flags;
	sigemptyset(&sa.sa_mask);
	if (sigaction(sig, &sa, NULL) < 0)
		debug_log(3, "sig=%d func=%p: %s failed\n", sig, func);
#else
	signal(sig, func);
#endif
}
